#ifndef _INODE_H
#define _INODE_H

#include <cnix/types.h>
#include <cnix/list.h>
#include <cnix/wait.h>
#include <cnix/select.h>
#include <cnix/stat.h>
#include <cnix/direct.h>

#define NR_INODE	2048
#define NR_INODE_HASH	(NR_INODE / 32)

#define NOINO		0x0
#define ROOT_INODE	0x2

#define REAL_ROOT(ino)	(ino == task[INITPID]->root)	

struct inode;

struct inode_operation{
	void (*free)(struct inode *);
	BOOLEAN (*can_delete)(struct inode *);
	int (*truncate)(struct inode *, loff_t);
	loff_t (*size)(struct inode *);
	void (*setsize)(struct inode *, loff_t);
	mode_t (*mode)(struct inode *);
	void (*setmode)(struct inode *, mode_t);
	dev_t (*realdev)(struct inode *);
	BOOLEAN (*real_filename)(char * page, struct inode *, int * error);
	BOOLEAN (*minode)(struct inode *, off_t, unsigned long);
	BOOLEAN (*noitem)(struct inode *);
	BOOLEAN (*additem)(struct inode *, const char *, int *, struct inode*);
	BOOLEAN (*delitem)(struct inode *, const char *, int *);
	ino_t (*findson)(struct inode *, const char *);
	uid_t (*getuid)(struct inode * inoptr);
	void (*setuid)(struct inode *, uid_t);
	gid_t (*getgid)(struct inode *);
	void (*setgid)(struct inode *, gid_t);
	void (*mknod)(struct inode *, dev_t);
	unsigned long (*bmap)(struct inode *, loff_t);
	int (*blocksize)(struct inode *);
	loff_t (*max_filesize)(struct inode *);
	void (*update)(struct inode *, time_t, time_t, time_t);
	nlink_t (*change_nlinks)(struct inode *, int);
	int (*get_name_of_son)(struct inode *, ino_t, char *, int);
	int (*stat)(struct inode *, struct stat *);
	int (*readdir)(int, struct dirent *);
	int (*readlink)(struct inode *,char *buf,size_t buf_size);
};

#define SOCKET	1
#define PIPE	2
#define ONHD	3 // file or dir

#define is_socket(inode) (inode->i_type == SOCKET)
#define is_pipe(inode) (inode->i_type == PIPE)
#define is_onhd(inode) (inode->i_type == ONHD)

#define MOUNT_I		0x01
#define SEEK_I		0x02
#define LOCK_I		0x04
#define WANT_I		0x08
#define MOUNTED_I	0x10

#define ATIME	01 // change access time
#define MTIME	02 // change modify time
#define CTIME	04 // change create time 

struct inode{
	char i_type;	// socket, pipe, file or dir
	char i_dirty;
	char i_update;
	char i_flags;	// LOCK WANT MOUNT SEEK

	dev_t i_dev;
	ino_t i_num;
	ino_t i_fnum;

	int i_count;

	list_t i_hashlist;
	list_t i_freelist;

	pid_t	i_lockowner;
	struct wait_queue * i_wait;

	union{
		struct{
			struct inode_operation * i_ops;
			void * i_data;
			struct super_block * i_sb;
			struct super_block * i_mounts;	// if MOUNT
		}s1;

		struct{
			off_t i_pipesize;
			int i_buddy;			// READBLE, WRITABLE
			unsigned char * i_buffer;	// for pipe
			short i_readp;
			short i_writep;
			select_t i_select;
		}s2;
	}u;
};

#define i_ops		u.s1.i_ops
#define i_data		u.s1.i_data
#define i_sb		u.s1.i_sb
#define i_mounts	u.s1.i_mounts

#define i_pipesize	u.s2.i_pipesize
#define i_buddy		u.s2.i_buddy
#define i_buffer	u.s2.i_buffer
#define i_readp		u.s2.i_readp
#define i_writep	u.s2.i_writep
#define i_select	u.s2.i_select

// bits for i_mode
#define I_TYPE	0170000	// inode type mode
#define I_SOCK	0140000 // socket
#define I_LNK	0120000 // symbolic link
#define I_REG	0100000	// regular file
#define I_BLK	0060000	// block special file
#define I_DIR	0040000	// directory
#define I_CHR	0020000	// character special file
#define I_FIFO	0010000 // named pipe

#define S_ISSOCK(m)	((m & I_TYPE) == I_SOCK)
#define S_ISLNK(m)	((m & I_TYPE) == I_LNK)
#define S_ISREG(m)	((m & I_TYPE) == I_REG)
#define S_ISBLK(m)	((m & I_TYPE) == I_BLK)
#define S_ISDIR(m)	((m & I_TYPE) == I_DIR)
#define S_ISCHR(m)	((m & I_TYPE) == I_CHR)
#define S_ISFIFO(m)	((m & I_TYPE) == I_FIFO)

#define I_SU	0004000	// set effective uid on exec
#define I_SG	0002000	// set effective gid on exec

#define I_UGRWX	0006777

#define I_RWX	0000777
#define I_RB	0000004
#define I_WB	0000002
#define I_XB	0000001
#define I_FREE	0000000

extern struct inode * iget(dev_t, ino_t);
extern void iput(struct inode *);
extern void ilock(struct inode *);
extern struct inode * ialloc(struct inode*, mode_t, int *);
extern void ifree(struct super_block *, ino_t);
extern int get_iiucount(dev_t);
extern struct inode_operation dummy_inode_operation;
extern void inval_inode(dev_t);

#endif
